﻿#include <iostream>
#include <fstream>
#include <cstring>
#include <vector>
#include <winsock2.h>
#include <windows.h>
#include "ikcp.h"
#include "sender.h"

static inline uint32_t ikcp_getms() {
    return GetTickCount();
}

int udp_output(const char *buf, int len, ikcpcb *kcp, void *user) {
    kcpUser kcpUserTmp = *(kcpUser*)user;
    int sockfd = kcpUserTmp.sockfd_local;
    struct sockaddr_in dest_addr;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(REMOTE_PORT);
    dest_addr.sin_addr.s_addr = inet_addr(REMOTE_IP);
    int ret = sendto(sockfd, buf, len, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
    if (ret < 0) {
        printf("[UDP] 发送失败: %d\n", WSAGetLastError());
    } else {
        printf("[UDP] 发送 %d 字节到 %s:%d\n", ret, REMOTE_IP, REMOTE_PORT);
    }
    return ret;
}

// KCP 更新线程函数
DWORD WINAPI kcp_update_thread(LPVOID lpParam) {
    ikcpcb* kcp = (ikcpcb*)lpParam;
    while (true) {
        ikcp_update(kcp, ikcp_getms());
        Sleep(10); // 每 10ms 更新一次
    }
    return 0;
}

// UDP 接收线程函数
DWORD WINAPI udp_receive_thread(LPVOID lpParam) {
    kcpUser kcpUserTmp = *(kcpUser*)lpParam;
    int sockfd = kcpUserTmp.sockfd_local;
    char buffer[KCP_MTU + 16];
    struct sockaddr_in src_addr;//记录发送方地址
    int addr_len = sizeof(src_addr);

    while (true) {
        int recv_len = recvfrom(sockfd, buffer, KCP_MTU + 16, 0, (struct sockaddr*)&src_addr, &addr_len);
        if (recv_len > 0) {
            // 将接收到的数据传递给 KCP 实例
            ikcpcb* kcp = kcpUserTmp.kcp;
            int ret = ikcp_input(kcp, buffer, recv_len);
            if (ret < 0) {
                printf("[KCP] 输入数据失败, ret=%d\n", ret);
            } else {
                printf("[KCP] 成功输入数据包, 长度=%d\n", recv_len);
            }
        } else {
           // printf("[UDP] 接收失败: %d\n", WSAGetLastError());
        }
    }
    return 0;
}

//发送文件 以线程方式独立调用 输入是文件数组指针
//没有类型的指针，也就是说你可以将任意类型的指针赋值给LPVOID类型的变量
DWORD WINAPI sendFile(LPVOID lpParam) {
    std::string filename = *(std::string*)lpParam;
    //0x11223344 8位的十六进制数 占用4字节 lpParam 是地址数值
    uint32_t conv = 0x11223344 + (uint32_t)lpParam;//拼接出随机的conv值
    kcpUser kcpUserTmp;
    kcpUserTmp.conv = conv;
    kcpUserTmp.sockfd_local = sockfd_local;
    // 创建 KCP 实例 
    ikcpcb* kcp = ikcp_create(conv, (void*)&kcpUserTmp);
    kcpUserTmp.kcp = kcp;
    kcp->output = udp_output;
    ikcp_wndsize(kcp, 256, 256); // 增大窗口大小
    ikcp_nodelay(kcp, 1, 10, 2, 1); // 启用无延迟模式
    printf("[KCP] 创建 KCP 实例, conv=%u\n", conv);

    // 创建 KCP 更新线程
    // HANDLE hUpdateThread = CreateThread(NULL, 0, kcp_update_thread, (LPVOID)kcp, 0, NULL);
    // if (hUpdateThread == NULL) {
    //     printf("创建 KCP 更新线程失败\n");
    //     ikcp_release(kcp);
    //     closesocket(sockfd_local);
    //     return 1;
    // }

    // 创建 UDP 接收线程
    HANDLE hReceiveThread = CreateThread(NULL, 0, udp_receive_thread, (LPVOID)kcp, 0, NULL);
    if (hReceiveThread == NULL) {
        printf("创建 UDP 接收线程失败\n");
        //TerminateThread(hUpdateThread, 0);
        //CloseHandle(hUpdateThread);
        ikcp_release(kcp);
        closesocket(sockfd_local);
        return 1;
    }

    // 发送控制包
    char ctrl_buf[16 + filename.size()];
    memcpy(ctrl_buf, CONTROL_MAGIC, MAGIC_LEN);
    uint16_t name_len = htons(filename.size());
    memcpy(ctrl_buf + MAGIC_LEN, &name_len, 2);
    memcpy(ctrl_buf + MAGIC_LEN + 2, filename.c_str(), filename.size());
    ikcp_send(kcp, ctrl_buf, MAGIC_LEN + 2 + filename.size());
    ikcp_update(kcp, ikcp_getms());
    printf("[控制] 发送控制包, conv=%u, 文件名=%s\n", conv, filename.c_str());

    // 等待确认回执
    char ack_buf[8];
    int ack_len = 0;
    int retry_count = 0;
    while (ack_len <= 0 && retry_count < 3) {
        ikcp_update(kcp, ikcp_getms());
        printf("检查服务端回执第%d次\n",retry_count);
        ack_len = ikcp_recv(kcp, ack_buf, sizeof(ack_buf));
        if (ack_len > 0) {
            char ack_magic[MAGIC_LEN + 1];
            memcpy(ack_magic, ack_buf, MAGIC_LEN);
            ack_magic[MAGIC_LEN] = '\0';
            uint32_t ack_conv = ntohl(*(uint32_t*)(ack_buf + MAGIC_LEN));
            if (strcmp(ack_magic, ACK_MAGIC) == 0 && ack_conv == conv) {
                printf("[ACK] 收到开始 ACK 回执, conv=%u\n", conv);
                break;
            } else {
                printf("[ACK] 无效的 ACK 包\n");
            }
        } else {
            printf("[ACK] 未收到 ACK 回执, 重试中...\n");
            retry_count++;
            Sleep(1000); // 等待1秒后重试
        }
    }

    if (ack_len <= 0) {
        printf("[ACK] 未收到 ACK 回执, 文件传输失败\n");
        //TerminateThread(hUpdateThread, 0);
        //CloseHandle(hUpdateThread);
        TerminateThread(hReceiveThread, 0);
        CloseHandle(hReceiveThread);
        ikcp_release(kcp);
        closesocket(sockfd_local);
        return 1;
    }

    // 读取文件数据并发送
    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        printf("打开文件失败: %s\n", filename.c_str());
        //TerminateThread(hUpdateThread, 0);
        //CloseHandle(hUpdateThread);
        TerminateThread(hReceiveThread, 0);
        CloseHandle(hReceiveThread);
        ikcp_release(kcp);
        closesocket(sockfd_local);
        return 1;
    }

    file.seekg(0, std::ios::end);
    size_t file_size = file.tellg();
    file.seekg(0, std::ios::beg);
    printf("[文件] 打开文件: %s, 大小=%zu 字节\n", filename.c_str(), file_size);

    char *file_data = new char[file_size];
    file.read(file_data, file_size);
    file.close();

    size_t sent = 0;
    while (sent < file_size) {
        int len = std::min((size_t)KCP_MTU, file_size - sent);
        char data_buf[MAGIC_LEN + len];
        memcpy(data_buf, DATA_MAGIC, MAGIC_LEN);
        memcpy(data_buf + MAGIC_LEN, file_data + sent, len);
        ikcp_send(kcp, data_buf, MAGIC_LEN + len);
        sent += len;
        ikcp_update(kcp, ikcp_getms());
        printf("[KCP] 发送 %d 字节, conv=%u, 已发送=%zu/%zu\n", len, conv, sent, file_size);
    }

    delete[] file_data;

    // 发送结束标志
    char end_buf[MAGIC_LEN];
    memcpy(end_buf, CONTROL_MAGIC, MAGIC_LEN);
    ikcp_send(kcp, end_buf, MAGIC_LEN);
    ikcp_update(kcp, ikcp_getms());
    printf("[控制] 发送结束包, conv=%u\n", conv);

    // 等待确认回执
    ack_len = 0;
    retry_count = 0;
    while (ack_len <= 0 && retry_count < 3) {
        ack_len = ikcp_recv(kcp, ack_buf, sizeof(ack_buf));
        if (ack_len > 0) {
            char ack_magic[MAGIC_LEN + 1];
            memcpy(ack_magic, ack_buf, MAGIC_LEN);
            ack_magic[MAGIC_LEN] = '\0';
            uint32_t ack_conv = ntohl(*(uint32_t*)(ack_buf + MAGIC_LEN));
            if (strcmp(ack_magic, ACK_MAGIC) == 0 && ack_conv == conv) {
                printf("[ACK] 收到结束 ACK 回执, conv=%u\n", conv);
                break;
            } else {
                printf("[ACK] 无效的 ACK 包\n");
            }
        } else {
            printf("[ACK] 未收到 ACK 回执, 重试中...\n");
            retry_count++;
            Sleep(1000); // 等待1秒后重试
        }
    }

    if (ack_len <= 0) {
        printf("[ACK] 未收到 ACK 回执, 文件传输失败\n");
    }

    // 终止线程并释放资源
    //TerminateThread(hUpdateThread, 0);
    //CloseHandle(hUpdateThread);
    TerminateThread(hReceiveThread, 0);
    CloseHandle(hReceiveThread);
    ikcp_release(kcp);
    closesocket(sockfd_local);
    printf("[KCP] 文件传输完成, conv=%u\n", conv);
    return 0;
}

int main() {
    // 设置控制台编码为 UTF-8
    SetConsoleOutputCP(65001);
    //这个结构被用来存储 被WSAStartup函数调用后返回的 Windows Sockets数据
    WSADATA wsaData;
    printf("初始化 Winsock \n");
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("初始化 Winsock 失败\n");
        return 1;
    }
//数据报格式套接字（Datagram Sockets）也叫“无连接的套接字”，在代码中使用 SOCK_DGRAM 表示。
    sockfd_local = socket(AF_INET, SOCK_DGRAM, 0);
    //INVALID_SOCKET ： 表示该 socket fd 无效
    if (sockfd_local == INVALID_SOCKET) {
        printf("创建套接字失败!\n");
        return 1;
    }
//初始化本地udp套接字
    struct sockaddr_in local_addr;
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(LOCAL_PORT);
    //本地绑定随机地址
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //将地址绑定到本地发送套接字上
    bind(sockfd_local, (struct sockaddr*)&local_addr, sizeof(local_addr));


    // 读取多个文件的列表
    std::vector<std::string> files = {"file1.txt", "file2.txt", "file3.txt"};
    std::vector<HANDLE> threads;
    for (size_t i = 0; i < files.size(); i++) {
        HANDLE hThread = CreateThread(NULL, 0, sendFile, (LPVOID)&files[i], 0, NULL);
        if (hThread == NULL) {
            printf("创建线程失败, 文件: %s\n", files[i].c_str());
            continue;
        }
        //一个新的元素加到vector的最后面，位置为当前最后一个元素的下一个元素
        threads.push_back(hThread);
    }

    // 等待所有线程完成 对象数量、句柄数组指针、
    //等待的类型，如果为TRUE，表示除非对象都发出信号，否则就一直等待下去；如果FALSE，表示任何对象发出信号即可 
    //指定要等候的毫秒数。如设为零，表示立即返回。如指定常数INFINITE，则可根据实际情况无限等待下去函数的返回值有：
    WaitForMultipleObjects(threads.size(), threads.data(), TRUE, INFINITE);
    //threads是数组 hThread是实例
    for (HANDLE hThread : threads) {
        printf("关闭一个线程实例\n");
        CloseHandle(hThread);
    }
    printf("清除socket数据");
    WSACleanup();
    return 0;
}